#!/usr/bin/python
import os
import math
import cherrypy
import ConfigParser

from pygooglechart import GroupedHorizontalBarChart, Axis

conf = os.path.join(os.path.dirname(__file__), 'glwebstats.conf')

# TODO:
#   smart stats (kb vs mb vs gb etc)
#   round up x_max integer to find max total to make graphs pretty
#   add notes on naiveness

statnames = ['ALLUP', 'ALLDN', 'DAYUP', 'DAYDN', 'WKUP', 'WKDN',
             'MONTHUP', 'MONTHDN']

class XferStat:
    files = 0
    kib = 0
    seconds = 0

class User:
    def __init__(self, name):
        self.username = name
        self.xfer = { }
        for statname in statnames:
            self.xfer[statname] = XferStat()
        return

class Root:
    _cp_config = { 'tools.staticdir.on': True,
                   'tools.staticdir.dir': os.path.abspath(os.path.dirname(__file__)),
                 }

    @cherrypy.expose
    def index(self):
        stats = self.read_stats()
        chartnames = self.gen_charts(stats)
        html = '<html><body><table>'
        lastuls = self.get_last_ul()
        html += '<tr><th colspan="2">LAST UL</th></tr>'
        i = 1
        for uls in lastuls:
            html += '<tr><td>%d:</td><td>%s</td></tr>' % (i, uls)
            i+=1

        html += '</table><table>'
        # find top ul/dn
        topulstats = sorted(stats, key = lambda u: u.xfer['ALLUP'].kib, reverse = True)
        topdnstats = sorted(stats, key = lambda u: u.xfer['ALLDN'].kib, reverse = True)
        html += '<tr><th colspan="3">TOPUP</th><th colspan="3">TOPDN</th></tr>'
        i = 1
        # XXX TODO when i implement "find_unit", make these stats prettier
        for (u,v) in zip(topulstats,topdnstats):
            html += '<tr><td>%d:</td><td>%s</td><td>%s KiB</td>' % (i, u.username, u.xfer['ALLUP'].kib)
            html += '<td>%d:</td><td>%s</td><td>%s KiB</td></tr>' % (i, v.username, v.xfer['ALLDN'].kib)
            i+=1

        html += '</table><table>'
        for name in chartnames:
            if name.find('UP') > -1:
                html += '<tr><td><img src="%s" /></td>' % name
            else:
                html += '<td><img src="%s" /></td></tr>' % name
        html += '</table></body></html>'
        return html

    def dirlist(self):
        try:
            conffp = open('glwebstats.conf', 'a+')
            config = ConfigParser.ConfigParser()
            config.readfp(conffp)
            usersdir = config.get('global', 'glftpd').replace('"', '').strip()
            usersdir = os.path.join(usersdir, 'users')
            userfiles = []
            for entryname in os.listdir(usersdir):
                entrypath = os.path.join(usersdir, entryname)
                if not os.path.isdir(entrypath):
                    if entryname != 'default.user' and entryname != 'glftpd':
                        userfiles.append(entrypath)
            return userfiles
        except IOError: #test this
            return

    def print_stat(self, stat):
        print 'user: ', stat.username
        for sname in statnames:
            print ' ', sname, stat.xfer[sname].files, stat.xfer[sname].kib, stat.xfer[sname].seconds

    def parse_stats(self, line, user_stat):
        about = line.split()
        if about[0] in statnames:
            user_stat.xfer[about[0]].files = float(about[1])
            user_stat.xfer[about[0]].kib = float(about[2])
            user_stat.xfer[about[0]].seconds = float(about[3])

    def read_stats(self):
        userfiles = self.dirlist()
        userstats = []
        for userfile in userfiles:
            try:
                userfh = open(userfile, 'r')
                user_stat = User(os.path.basename(userfile))
                for line in userfh.readlines():
                    self.parse_stats(line, user_stat)
                userstats.append(user_stat)
                userfh.close()
                #self.print_stat(user_stat)
            except IOError: #XXX
                return
        
        return userstats

    def get_last_ul(self):
        try:
            conffp = open('glwebstats.conf', 'a+')
            config = ConfigParser.ConfigParser()
            config.readfp(conffp)
            logsdir = config.get('global', 'glftpd').replace('"', '').strip()
            logsdir = os.path.join(logsdir, 'logs')
            xferlog = os.path.join(logsdir, 'xferlog')
            xferlogfh = open(xferlog, 'r')
            xferlogfh.seek(0, 2) # seek to eof
            fsize = xferlogfh.tell()
            xferlogfh.seek(max(fsize-4096, 0), 0)
            lines = xferlogfh.readlines()
            lines = lines[-10:]
            uls = []
            for line in lines:
                if line.find('b _ i') > -1:
                    line = line.split()
                    uls.append(line[-9])

            uls = list(set(uls))
            uls.reverse()
            print uls
            return uls
        except IOError: # XXX
            return
            

    def max_kib_val(self, stats, sname):
        maxval = 0
        for stat in stats:
            if stat.xfer[sname].kib > maxval:
                maxval = stat.xfer[sname].kib
        maxval = math.ceil(maxval / 100) * 100
        maxval = math.ceil(maxval / 1024 / 1024)
        return float(maxval)

    def gen_data(self, stats, sname):
        names = []
        values = []
        for stat in stats:
            names.append(stat.username)
            values.append(stat.xfer[sname].kib / 1024 / 1024) # XXX same
# division constatn as maxval
        names.reverse() # necessary due to horizontal axis value ordering...
        return (names, values)

    def gen_charts(self, stats):
        chartnames = []
        i = 0
        for sname in statnames:
            # X add debug logging
#            print 'generating chart for', sname
            xmax = self.max_kib_val(stats, sname)
            if xmax == 0.0:
                continue
            chart = GroupedHorizontalBarChart(500, 250, x_range=(0.0, xmax))
            chart.set_bar_width(10)
            chart.set_bar_spacing(2)
            chart.set_colours(['4D89F9'])
            (names, values) = self.gen_data(stats, sname)
            chart.set_title(sname + ' (GiB)')
            chart.add_data(values)
            chart.set_axis_labels(Axis.LEFT, names)
            botaxis = []
            # XXX make this less granular than /4 and update also
            # chart=GroupedHorizontalBarChat
            for i in range(0, 6):
                botaxis.append('%.2f' % (float(i) * xmax/5))
            chart.set_axis_labels(Axis.BOTTOM, botaxis)
            chartname = '%s.png' % sname
            chart.download(chartname)
            chartnames.append(chartname)
        return chartnames

            

if __name__ == '__main__':
    cherrypy.quickstart(Root(), config=conf)
else:
    cherrypy.tree.mount(Root(), config=conf)
